/** \brief File declaring a class managing a matrix of Cell instances.
 *
 * This file contains the declaration of a matrix of Cell instances.
 *
 * @file GridBase.hpp
 * @author Johan "Solo" Luisier
 * @date 2013/03/22
 */
#ifndef GRIDBASE_HPP
#define GRIDBASE_HPP

#include <QtCore/QHash>
#include <QtCore/QList>
#include <QtCore/QObject>
#include <QtCore/QPoint>
#include <QtCore/QVector>

class Cell;
class RuleData;

/** @brief The GridBase class implements a basic grid of cells.
 *
 * This class implements a grid of cells. This is an abstract class because
 * there are some features which depend on the real geometry of the grid (the
 * number of neighbours and their relative positions for instance).
 *
 * @author Johan "Solo" Luisier
 * @date 2013/03/22
 *
 * @class GridBase "GridBase.hpp" "Core/GridBase.hpp"
 */
class GridBase : public QObject
{
    Q_OBJECT
public:

    /** @brief Standard constructor.
     *
     * @param[in] w
     * @param[in] h
     * @param[in] parent
     */
    GridBase( const quint32& w, const quint32& h,
              QObject* parent = 0 );
    /** @brief Default constructor.
     *
     * Don't forget to set the width and height of the grid before starting.
     *
     * @param[in] parent
     */
    GridBase( QObject* parent = 0 );
    virtual ~GridBase();

    /** @brief Getter for Running
     *
     * @return a const reference on Running.
     */
    const bool& isRunning() const;
    /** @brief Getter for Width.
     *
     * @return a const reference on Width.
     */
    const quint32& width() const;
    /** @brief Setter for Width.
     *
     * @param[in] w new value for Width;
     */
    void setWidth( const quint32& w );
    /** @brief Getter for Height.
     *
     * @return a const reference on Height.
     */
    const quint32& height() const;
    /** @brief Setter for Height.
     *
     * @param[in] h new value for Height;
     */
    void setHeight( const quint32& h );
    /** @brief Getter for CellNumber.
     *
     * The @e real number of cells depends on the geometry, some of them aren't
     * used for instance with diamonds tiles. The CellNumber is initialised by
     * the geometry-specific method which sets the neighbours.
     *
     * @return a const reference on CellNumber.
     */
    const quint32& cellNumber() const;
    /** @brief Getter for an element of Matrix.
     *
     * @param[in] i
     * @param[in] j
     *
     * @retval ptr a const pointer on the wanted element if it exists, @retval
     * 0 if not.
     */
    const Cell* at( const quint32& i, const quint32& j ) const;
    /** @brief Getter for an element of Matrix.
     *
     * @param[in] i
     * @param[in] j
     *
     * @retval ptr a pointer on the wanted element if it exists, @retval 0 if
     * not.
     */
    Cell* operator()( const quint32& i, const quint32& j );
    /** @brief Getter for the maximal number of neighbours of a Cell.
     *
     * This method allows to access the maximal number of neighbours for a Cell
     * instance. Since this number is model-dependent, the method is pure
     * virtual.
     *
     * @return a const reference on the maximal number of neighbours.
     */
    virtual const quint32& maxNeighbourNumber() const = 0;
    /**
     */
    inline const RuleData* gameRules() const { return GameRules; }
    void setGameRules( RuleData* ptr ) { GameRules = ptr; }
    void setGameRules( const RuleData& instance ) { GameRules = const_cast< RuleData* >( &instance ); }

public slots:

    /** @brief The processing slot.
     *
     * This slot is responsible for the computation of the future statuses of
     * the cells and their evolution.
     *
     * This can be multithreaded if GridBase::MaxThreads is greater than one.
     */
    void step();

protected:

    /** @brief Contains @c true when the instance is ready to run.
     */
    bool Running;
    /** @brief Width of the grid of Cell instances.
     *
     * This is the number of cells appearing on the visualisation widget along
     * the @f$x@f$ axis.
     */
    quint32 Width;
    /** @brief Height of the grid of Cell instances.
     *
     * This is the number of cells appearing on the visualisation widget along
     * the @f$y@f$ axis.
     */
    quint32 Height;
    /** @brief This is the @e real number of active cells.
     *
     * Depending on the geometry, some Cell instances may exist but aren't
     * used. This number correspond to the number of Cell instances which are
     * updated and displayed.
     */
    quint32 CellNumber;
    /** @brief "Matrix" of Cell instances.
     *
     * This container holds the @e allocated Cell instance pointers. At the
     * moment the indexing corresponds to the position in the visualisation in
     * the following way: the cell at @f$ i, j @f$ lies at the position @f$ i
     * \cdot Width + j @f$.
     */
    QVector< Cell* > Matrix;
    /** @brief Pointer on the game rules.
     *
     * This holds a pointer on the instance of the game rules.
     */
    RuleData* GameRules;
    /** @brief Container associating each Cell pointer to its position in the
     * grid.
     *
     * This container is used in the cellFlipped slot, to know rapidly which
     * coordinates correspond to the flipped Cell instance.
     */
    QHash< Cell*, QPoint > CellMapping;

private:

    /** @brief Method creating the matrix of Cell instances.
     *
     * This method creates the matrix of Cell instances, and then delegates the
     * creation of connections to initialiseNeighbours().
     *
     * @retval true if the creation when without any problem, @retval false if
     * the matrix dimensions are not initialised or of initialiseNeighbours()
     * could not run properly.
     */
    bool initialiseMatrix();
    /** @brief Method creating the connections between Cell instances.
     *
     * This method sets for each Cell instance the neighbours it has. If an
     * instance won't be represented on the graphic visualisation, then it has
     * @e no neighbours and is the neighbour of no other instance.
     *
     * @retval true if no errors occured, @retval false if something went wrong.
     */
    virtual bool initialiseNeighbours() = 0;

private slots:

    /** @brief The initialise slot.
     *
     * This slot initialises the Matrix member by calling initialiseMatrix, and
     * stores the result in the Running member.
     */
    void initialise();

    /** @brief The cell flipping detecting slot.
     *
     * This slot allows to detect when a Cell instance is flipped. It retrieves
     * the coordinate of this instance in the grid.
     *
     * This method emits flipped signal.
     *
     * @param cell pointer on the flipped Cell instance.
     */
    void cellFlipped( Cell* cell );
    /** @brief The cell flip slot.
     *
     * This slot allows to flip a Cell instance. The method checks that the
     * point is realy `in' the grid and that the Cell has neighbours.
     *
     * @param[in] pt coordinates of the Cell instance to flip.
     */
    void flipCell( const QPoint& pt );

signals:

    /** @brief The step completion signal.
     *
     * This signal is sent when the step processing is done.
     */
    void stepComputed();
    /** @brief The Cell flipped signal.
     *
     * This signal is sent when a Cell instance is flipped.
     *
     * @param coord coordinates of the cell in the grid.
     */
    void flipped( const QPoint& coord );
};

#endif // GRIDBASE_HPP
